package controller.simulationcontroller;

import java.awt.Container;
import java.awt.Cursor;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;

import javax.swing.JDialog;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JViewport;
import javax.swing.event.MouseInputListener;

import locsi.UserSettings;
import model.LogicType;
import model.editormodel.LogicEditorElement;
import model.editormodel.LogicSelfdefinedEditorElement;
import model.simulationmodel.SimulationModel;
import view.commonview.ButtonValues;
import view.commonview.LogicViewElement;
import view.commonview.SelfdefinedPicturePanel;
import view.commonview.TabbedPane;
import view.commonview.ToolBar;
import view.simulationview.SimulationArea;

/**
 * <b>Implementiert:</b> <code>MouseInputListener</code> <br>
 * <b>In Verbindung stehende Klassen:</b>
 * <code>SimulationArea, ToolBar, SimulationModel, TabbedPane</code><br>
 * <br>
 * Listener, der auf der <code>SimulationArea</code> liegt. Je nach Toolmodus
 * werden entsprechende Methoden (z.B. zum Setzen der Eingaenge) im
 * <code>SimulationModel</code> aufgerufen.
 * 
 * @author Team Locsi
 * 
 */
public class SimulationAreaListener implements MouseInputListener {

	private SimulationArea sArea;
	private ToolBar toolBar;
	private SimulationModel sModel;
	private TabbedPane tabs;
	private double moveX;
	private double moveY;
	// Punkt: eigentlicher Klickpunkt mit Zoomfaktor verrechnet
	private Point2D mewz = new Point2D.Double();
	// Cursor
	private Cursor move = new Cursor(Cursor.MOVE_CURSOR);
	private Cursor hand = new Cursor(Cursor.HAND_CURSOR);
	private Cursor defaultC = new Cursor(Cursor.DEFAULT_CURSOR);
	private JDialog showPicPanel;

	/**
	 * Dieser Konstruktor erzeugt ein neues <code>SimulationAreaListener</code>
	 * -Objekt.
	 * 
	 * @param sArea
	 *            <code>SimulationArea</code> - Referenz auf die
	 *            <code>SimulationArea</code>
	 * @param toolBar
	 *            <code>ToolBar</code> - Referenz auf die <code>ToolBar</code>
	 * @param sModel
	 *            <code>SimulationModel</code> - Referenz auf das
	 *            <code>SimulationModel</code>
	 * @param tabs
	 *            <code>TabbedPane</code> - Referenz auf das
	 *            <code>TabbedPane</code>
	 */
	public SimulationAreaListener(SimulationArea sArea, ToolBar toolBar,
			SimulationModel sModel, TabbedPane tabs) {
		this.sArea = sArea;
		this.toolBar = toolBar;
		this.sModel = sModel;
		this.tabs = tabs;
	}

	/**
	 * Je nach ausgewaehltem Modus werden bestimmte Operationen durchgefuehrt.
	 */
	@Override
	public void mouseClicked(MouseEvent e) {

		// eigentlichen angeklickter Punkt mit Zoomfaktor berechnen
		mewz.setLocation(e.getPoint().getX() / tabs.getZoomFactor(), e
				.getPoint().getY() / tabs.getZoomFactor());

		if (toolBar.getCurrentMode() == ButtonValues.SELECTION_MODE) {
			
			if (e.getButton() == MouseEvent.BUTTON3) {
				tabs.resetZoomFactor();
			}
			
			// angeklicktes Viewelement holen
			final LogicViewElement vElem = this.sArea.containsElement(mewz);

			if (vElem != null) {

				// falls es sich beim angeklickten Element um einen Schalter
				// handelt, wird der eingestellte Wert umgekehrt
				if (this.sModel.getLogicElements().get(vElem.getElementsName())
						.getType() == LogicType.SWITCH) {
					this.sModel.changeSwitchValue(vElem.getElementsName());

					// falls es sich beim angeklickten Elememt um einen
					// Taktgeber handelt, wird ueberprueft ob linke oder rechte
					// Maustaste
					// gedrueckt worden ist
				} else if (this.sModel.getLogicElements()
						.get(vElem.getElementsName()).getType() == LogicType.CLOCK) {

					// wenn linke Maustaste dedrueckt worden ist wird der
					// eingestellte Wert umgekehrt
					if (e.getButton() == MouseEvent.BUTTON1) {
						this.sModel.changeClockValue(vElem.getElementsName());

						// ist die rechte Maustaste gedrueckt worden, wird ein
						// Pop-up zur Auswahl der Frequenzen geoeffnet
					} else if (e.getButton() == MouseEvent.BUTTON3) {
						JPopupMenu popUp = new JPopupMenu();
						JMenuItem[] items = new JMenuItem[5];
						double frequency = 0.25;
						for (int i = 0; i < items.length; i++) {
							items[i] = new JMenuItem(String.valueOf(frequency)
									+ " Hz");
							items[i].setActionCommand(String.valueOf(frequency));
							frequency = frequency * 2;
							popUp.add(items[i]);
							items[i].addActionListener(new SetFrequencyListener(
									sModel, vElem));
							popUp.add(items[i]);
						}
						popUp.show(sArea, ((int) e.getX()),
								((int) e.getY()));
					}
				}
			}
		}
	}

	/**
	 * Je nach ausgewaehltem Modus werden bestimmte Operationen durchgefuehrt.
	 */
	@Override
	public void mouseEntered(MouseEvent e) {

		// setzt verschiedene Cursor je nach ausgewaehlten Modus
		switch (this.toolBar.getCurrentMode()) {
		case SELECTION_MODE:
			this.sArea.setCursor(defaultC);
			break;
		case MOVE_MODE:
			this.sArea.setCursor(move);
			break;
		default:
			this.sArea.setCursor(defaultC);
			break;
		}
	}

	/**
	 * Je nach ausgewaehltem Modus werden bestimmte Operationen durchgefuehrt.
	 */
	@Override
	public void mouseExited(MouseEvent e) {
		// wird nicht benoetigt
	}

	/**
	 * Je nach ausgewaehltem Modus werden bestimmte Operationen durchgefuehrt.
	 */
	@Override
	public void mousePressed(MouseEvent e) {

		// eigentlichen angeklickter Punkt mit Zoomfaktor berechnen
		mewz.setLocation(e.getPoint().getX() / tabs.getZoomFactor(), e
				.getPoint().getY() / tabs.getZoomFactor());

		// Koordinaten vom Klick speichern!
		if (toolBar.getCurrentMode() == ButtonValues.MOVE_MODE) {
			moveX = mewz.getX() * tabs.getZoomFactor();
			moveY = mewz.getY() * tabs.getZoomFactor();
		}
	}

	/**
	 * Je nach ausgewaehltem Modus werden bestimmte Operationen durchgefuehrt.
	 */
	@Override
	public void mouseReleased(MouseEvent e) {
		// wird nicht benoetigt
	}

	/**
	 * Je nach ausgewaehltem Modus werden bestimmte Operationen durchgefuehrt.
	 */
	@Override
	public void mouseDragged(MouseEvent e) {

		// veraendern des jviewports des jscrollpanes, auf dem die
		// simulationarea liegt
		if (toolBar.getCurrentMode() == ButtonValues.MOVE_MODE) {
			Container c = sArea.getParent();
			if (c instanceof JViewport) {
				JViewport jv = (JViewport) c;
				Point p = jv.getViewPosition();

				// neu Koordinaten fuer den ViewPort Berechnen
				// Korrektur durch moveX und moveY
				int newX = p.x - (e.getX() - (int) moveX);
				int newY = p.y - (e.getY() - (int) moveY);

				int maxX = sArea.getWidth() - jv.getWidth();
				int maxY = sArea.getHeight() - jv.getHeight();

				if (newX < 0) {
					newX = 0;
				} else if (newX > maxX) {
					newX = maxX;
				}
				if (newY < 0) {
					newY = 0;
				} else if (newY > maxY) {
					newY = maxY;
				}
				jv.setViewPosition(new Point(newX, newY));
			}
		}
	}

	/**
	 * Je nach ausgewaehltem Modus werden bestimmte Operationen durchgefuehrt.
	 */
	@Override
	public void mouseMoved(MouseEvent e) {

		if (toolBar.getCurrentMode() == ButtonValues.SELECTION_MODE) {

			// eigentlichen Punkt mit Zoomfaktor berechnen
			mewz.setLocation(e.getPoint().getX() / tabs.getZoomFactor(), e
					.getPoint().getY() / tabs.getZoomFactor());

			LogicViewElement vElem = this.sArea.containsElement(mewz);
			if (vElem != null) {

				// wenn ueber Schalter oder Lampe gefahren wird -> Handcursor
				if (this.sModel.getLogicElements().get(vElem.getElementsName())
						.getType() == LogicType.SWITCH
						|| this.sModel.getLogicElements()
								.get(vElem.getElementsName()).getType() == LogicType.CLOCK) {
					this.sArea.setCursor(hand);
					this.sArea.getParent().setCursor(hand);
				}

				if (UserSettings.paintSDPicture) {
					if (!UserSettings.needControlDownForPicture
							|| e.isControlDown()) {
						LogicEditorElement lEE = sModel.getLogicElements().get(
								vElem.getElementsName());
						if (showPicPanel == null
								&& lEE instanceof LogicSelfdefinedEditorElement) {

							showPicPanel = new SelfdefinedPicturePanel(lEE,
									mewz, sArea.getiArea());
							showPicPanel.pack();
							showPicPanel.setLocation(e.getLocationOnScreen());
							showPicPanel.setVisible(true);
							sArea.revalidate();
							sArea.repaint();
						}
					}
				}

				// ansonsten defaultcursor
			} else {
				this.sArea.setCursor(defaultC);
				this.sArea.getParent().setCursor(defaultC);
				if (showPicPanel != null
						&& !showPicPanel.getBounds().contains(
								(int) mewz.getX(), (int) mewz.getY())) {
					showPicPanel.dispose();
					showPicPanel = null;
					sArea.revalidate();
					sArea.repaint();
				}
			}
		}
	}
}